import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import java.awt.*;
import java.awt.event.*;
import java.util.LinkedList;
import java.util.Random;

public class GUI extends JFrame {

	public static void main(String[] args) {
		GUI g = new GUI();
	}

	ColorArea[] ColorAreas = new ColorArea[8];

	double mutationRate = 0.01;
	Color initialColor = Color.white;
	int default_crossover_method, evolve_version;
	static final int UNIFORM = 0, SingleFixed = 1, SingleRandomCA = 2,
			SingleRandomSA = 3, SingleRandomC = 4;
	static final int single_father = 0, multi_parents = 1;
	boolean first_open = true;
	ColorArea ca_chosed;
	Random rand = new Random();
	LinkedList<ColorArea> fathers = new LinkedList<ColorArea>();

	ColorArea slectedColorArea() {
		for (ColorArea ca : this.ColorAreas) {
			if (ca.selected)
				return ca;

		}
		return null;
	}

	public GUI() {
		super("HueMe!");
		evolve_version = multi_parents;
		// Container content = getContentPane();

		// what layout?
		// this.setLayout(new FlowLayout());
		default_crossover_method = this.SingleRandomSA;

		this.setLayout(new GridBagLayout());

		// color area action listener
		ChooseFatherMouseListener cfa = new ChooseFatherMouseListener();
		// adding the color areas

		ColorArea ca1 = new ColorArea();
		ca1.addMouseListener(cfa);
		GridBagConstraints c = new GridBagConstraints();
		// c.weightx = 1;
		// c.weighty = 1;
		c.fill = GridBagConstraints.BOTH;
		c.gridx = 0;
		c.gridy = 0;
		c.insets = new Insets(10, 10, 10, 10); // top padding

		this.add(ca1, c);
		ColorAreas[0] = ca1;

		ColorArea ca2 = new ColorArea();
		ca2.addMouseListener(cfa);
		c = new GridBagConstraints();
		// c.weightx = 1;
		// c.weighty = 1;
		c.fill = GridBagConstraints.BOTH;
		c.gridx = 1;
		c.gridy = 0;
		c.insets = new Insets(10, 10, 10, 10); // top padding

		this.add(ca2, c);
		ColorAreas[1] = ca2;

		ColorArea ca3 = new ColorArea();
		ca3.addMouseListener(cfa);
		c = new GridBagConstraints();
		// c.weightx = 1;
		// c.weighty = 1;
		c.fill = GridBagConstraints.BOTH;
		c.gridx = 2;
		c.gridy = 0;
		c.insets = new Insets(10, 10, 10, 10); // top padding

		this.add(ca3, c);

		ColorAreas[2] = ca3;

		ColorArea ca4 = new ColorArea();
		ca4.addMouseListener(cfa);
		c = new GridBagConstraints();
		// c.weightx = 1;
		// c.weighty = 1;
		c.fill = GridBagConstraints.BOTH;
		c.gridx = 0;
		c.gridy = 1;
		c.insets = new Insets(10, 10, 10, 10); // top padding

		this.add(ca4, c);
		ColorAreas[3] = ca4;

		ColorArea ca5 = new ColorArea();
		ca5.addMouseListener(cfa);
		c = new GridBagConstraints();
		// c.weightx = 1;
		// c.weighty = 1;
		c.fill = GridBagConstraints.BOTH;
		c.gridx = 2;
		c.gridy = 1;
		c.insets = new Insets(10, 10, 10, 10); // top padding

		this.add(ca5, c);
		ColorAreas[4] = ca5;

		ColorArea ca6 = new ColorArea();
		ca6.addMouseListener(cfa);
		c = new GridBagConstraints();
		// c.weightx = 1;
		// c.weighty = 1;
		c.fill = GridBagConstraints.BOTH;
		c.gridx = 0;
		c.gridy = 2;
		c.insets = new Insets(10, 10, 10, 10); // top padding

		this.add(ca6, c);
		ColorAreas[5] = ca6;

		ColorArea ca7 = new ColorArea();
		ca7.addMouseListener(cfa);
		c = new GridBagConstraints();
		// c.weightx = 1;
		// c.weighty = 1;
		c.fill = GridBagConstraints.BOTH;
		c.gridx = 1;
		c.gridy = 2;
		c.insets = new Insets(10, 10, 10, 10); // top padding

		this.add(ca7, c);
		ColorAreas[6] = ca7;

		ColorArea ca8 = new ColorArea();
		ca8.addMouseListener(cfa);
		c = new GridBagConstraints();
		// c.weightx = 1;
		// c.weighty = 1;
		c.fill = GridBagConstraints.BOTH;
		c.gridx = 2;
		c.gridy = 2;
		c.insets = new Insets(10, 10, 10, 10); // top padding

		this.add(ca8, c);
		ColorAreas[7] = ca8;

		// start Button
		// ImageIcon startIcon = new ImageIcon("image/start-icon.png");
		// JButton start_button = new JButton(startIcon);
		JButton start_button = new JButton("START");
		// start_button.setOpaque(false);

		// start_button.setBounds(20, 20, 20, 20);
		// start_button.setPreferredSize(new Dimension(50, 30));
		start_button.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				Color cl = JColorChooser.showDialog(
						((JButton) e.getSource()).getParent(),
						"Please Select a Main Color", Color.cyan);
				if (cl != null) {
					initialColor = cl;
					// right now, just set the first sub area's color the same
					// as
					// the chosen color
					for (ColorArea ca : ColorAreas) {

						ca.sa1.setColor(cl);
						ca.sa2.reset();
						ca.sa3.reset();
						ca.sa4.reset();
						ca.sa5.reset();
						ca.gene.update_gene();
						// System.out.println("result of start function:");
						// ca.gene.show_gene();
					}
				}

			}
		});

		// adding control panel
		JPanel cp = new JPanel();
		cp.setLayout(new GridLayout(4, 3, 10, 10));
		JLabel cross_over_label1 = new JLabel("Multi Fathers ");
		cp.add(cross_over_label1);
		JLabel cross_over_label2 = new JLabel("Uniform CrossOver");
		cross_over_label2.setAlignmentX(CENTER_ALIGNMENT);
		cp.add(cross_over_label2);
		// cp.add(start_button, BorderLayout.CENTER);
		cp.add(start_button);

		// update button
		// ImageIcon update_icon = new ImageIcon("image/update.gif");
		// JButton co_button = new JButton(update_icon);
		JButton co_button = new JButton("Evolve");
		// co_button.setHorizontalAlignment(JButton.);

		// co_button.setVerticalTextPosition(JButton.BOTTOM);
		// co_button.setIcon(update_icon);
		co_button.addActionListener(new evolveActionListener());

		cp.add(co_button);

		// mutation slider
		JSlider slider_mutation = new JSlider();
		slider_mutation.setBorder(BorderFactory
				.createTitledBorder("mutation rate"));
		// slider_mutation.setPaintTicks(true);
		slider_mutation.setPaintLabels(true);// why not show label;
		// slider_mutation.setPaintTrack(true);

		slider_mutation.addChangeListener(new ChangeListener() {

			@Override
			public void stateChanged(ChangeEvent e) {
				// TODO Auto-generated method stub
				mutationRate = ((JSlider) e.getSource()).getValue() / 5000.0;
				System.out.println(mutationRate);

			}
		});

		cp.add(slider_mutation);
		// mutate button
		JButton mutate_button = new JButton("mutate");
		cp.add(mutate_button);
		mutate_button.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				// JOptionPane.showMessageDialog(null, "mutation rate is "
				// + mutationRate
				// + "\n funtion haven't been implemented yet",
				// "mutation", JOptionPane.INFORMATION_MESSAGE);
				for (ColorArea ca : ColorAreas) {

					if (mutationRate < 0.01) {
						JOptionPane
								.showMessageDialog(
										null,
										"mutation rate is "
												+ mutationRate
												+ "\n it is too small, you'd better increase to see change",
										"mutation",
										JOptionPane.INFORMATION_MESSAGE);
						return;
					}
					GeneticAlgorithm.mutate(mutationRate, ca);
					System.out.println("mutate");
					ca.update_color();

				}

			}
		});

		// show button
		JButton show_button = new JButton("Clear");
		show_button.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				ca_chosed = null;
				Object[] options = { "Clear All Colors",
						"Clear Choosed Fathers" };
				int n = JOptionPane
						.showOptionDialog(null,
								"what kind of CLEAR do you want to do?",
								"Clear Option",
								JOptionPane.YES_NO_CANCEL_OPTION,
								JOptionPane.QUESTION_MESSAGE, null, options,
								options[1]);
				// System.out.println(n);
				if (n == 0) {
					for (ColorArea ca : ColorAreas) {
						ca.clear();
					}
				} else {
					for (ColorArea ca : ColorAreas) {
						if (ca.selected) {
							ca.selected = false;
							ca.setBorder(BorderFactory.createLineBorder(
									Color.white, 4));
						}
					}
				}

			}
		});
		cp.add(show_button);

		// restart button
		JButton restart_button = new JButton("restart");
		restart_button.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				// TODO Auto-generated method stub
				Object[] options = { "Yes", "Cancle" };

				int n = JOptionPane
						.showOptionDialog(
								null,
								"Restart will keep the main color\nBut will randomly reset other others\n Keep doing?",
								"Restart", JOptionPane.YES_NO_CANCEL_OPTION,
								JOptionPane.QUESTION_MESSAGE, null, options,
								options[1]);
				if (n == 0) {
					for (ColorArea ca : ColorAreas) {
						// ca.restart();
						// ca.sa1.setColor(initialColor);

						ca.sa2.reset();
						ca.sa3.reset();
						ca.sa4.reset();
						ca.sa5.reset();

						ca.gene.update_gene();
						ca.gene.show_gene();

					}
				}

			}
		});
		cp.add(restart_button);
		cp.setPreferredSize(new Dimension(228, 158));

		c = new GridBagConstraints();
		// c.weightx = 1;
		// c.weighty = 1;
		// c.gridwidth = 3;
		c.fill = GridBagConstraints.BOTH;

		c.gridx = 1;
		c.gridy = 1;
		c.insets = new Insets(10, 10, 10, 10); // top padding
		this.add(cp, c);

		// menu bar
		JMenuBar mb = new JMenuBar();
		JMenu fmenu = new JMenu("Edit");
		JMenuItem show_item = new JMenuItem("show RGB");

		show_item.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				ColorArea ca = slectedColorArea();
				if (ca != null) {

					JOptionPane.showMessageDialog(null, ca.toString(),
							"Show RGB", JOptionPane.INFORMATION_MESSAGE);

				} else {
					JOptionPane.showMessageDialog(null,
							"you need to choose one color combination",
							"Warning", JOptionPane.WARNING_MESSAGE);

				}

			}
		});
		fmenu.add(show_item);

		JMenu setting_menu = new JMenu("Setting");
		JMenu cross_over_setting = new JMenu("crossover setting");
		JMenuItem fix_single_point_item = new JMenuItem(
				"Fixed Single Point Cross Over");
		JMenuItem random_single_point_item_caLevel = new JMenuItem(
				"Random Single Point Cross Over Color Area level");
		JMenuItem random_single_point_item_saLevel = new JMenuItem(
				"Random Single Point Cross Over Sub Area Level");
		JMenuItem Random_single_point_item_cLevel = new JMenuItem(
				"Random Single Point Cross Over Color Level");
		JMenuItem Uniform_crossOver = new JMenuItem("Uniform Cross Over");
		cross_over_setting.add(fix_single_point_item);
		cross_over_setting.add(random_single_point_item_caLevel);
		cross_over_setting.add(random_single_point_item_saLevel);
		cross_over_setting.add(Random_single_point_item_cLevel);
		cross_over_setting.add(Uniform_crossOver);
		fix_single_point_item
				.addActionListener(new crossOverSettingActionListener(
						SingleFixed, cross_over_label2));
		random_single_point_item_caLevel
				.addActionListener(new crossOverSettingActionListener(
						SingleRandomCA, cross_over_label2));
		random_single_point_item_saLevel
				.addActionListener(new crossOverSettingActionListener(
						SingleRandomSA, cross_over_label2));
		Random_single_point_item_cLevel
				.addActionListener(new crossOverSettingActionListener(
						SingleRandomC, cross_over_label2));
		Uniform_crossOver.addActionListener(new crossOverSettingActionListener(
				UNIFORM, cross_over_label2));

		JMenu version_Menu = new JMenu(" Evolve Version Setting");
		JMenuItem single_father = new JMenuItem("Single Father");
		single_father.addActionListener(new EvolveSettingActionListener(
				GUI.single_father, cross_over_label1));
		JMenuItem multi_parents = new JMenuItem("Multi Parents");
		multi_parents.addActionListener(new EvolveSettingActionListener(
				GUI.multi_parents, cross_over_label1));
		version_Menu.add(single_father);
		version_Menu.add(multi_parents);
		setting_menu.add(version_Menu);
		setting_menu.add(cross_over_setting);

		mb.add(fmenu);
		mb.add(setting_menu);

		this.setJMenuBar(mb);

		this.setSize(800, 600);
		this.setLocation(200, 100);
		this.setVisible(true);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.pack();
		JOptionPane
				.showMessageDialog(
						null,
						"Hi, Welcome to use HueMe!\n\n"
								+ "HueMe will help you to generate \"Color Combination\" You likes.\n"
								+ "First of all, you need to choss a \"Main Color\" using \"START\" function \n"
								+ "Then, choose the color combinations you like and click \"Evole\" to generate new ones\n"
								+ "You can use \"Setting\" in tool bar to configue the software\n"
								+ "\nHope you enjoy using Hueme! \n",
						"Help Message", JOptionPane.INFORMATION_MESSAGE);

	}

	class ChooseFatherMouseListener extends MouseAdapter {

		public void mouseClicked(MouseEvent e) {
			// TODO Auto-generated method stub
			ColorArea ca = (ColorArea) e.getSource();
			if (evolve_version == single_father) {
				if (ca_chosed != null) {
					if (ca.equals(ca_chosed)) {
						ca.setBorder(BorderFactory.createLineBorder(
								Color.white, 4));
						// ca.setBorder(BorderFactory.createLineBorder(getBackground(),
						// 4));
						ca.selected = false;
					} else {
						ca_chosed.setBorder(BorderFactory
								.createLineBorder(Color.white));
						ca_chosed.selected = false;
						ca.setBorder(BorderFactory.createLineBorder(Color.blue,
								4));
						// ca.setBorder(BorderFactory.createRaisedBevelBorder());
						// ca.setBorder(BorderFactory.createEmptyBorder());

						ca.selected = true;
						ca_chosed = ca;

					}
				} else {// multi parents version
					ca.setBorder(BorderFactory.createLineBorder(Color.blue, 4));
					// ca.setBorder(BorderFactory.createRaisedBevelBorder());

					ca.selected = true;
					ca_chosed = ca;

				}
			}
			if (evolve_version == multi_parents) {
				// JOptionPane.showMessageDialog(null,
				// "multi not implemented yet", "sorry",
				// JOptionPane.WARNING_MESSAGE);
				if (ca.selected) {
					ca.setBorder(BorderFactory.createLineBorder(Color.white, 4));
					ca.selected = false;
					ca.count = 0;
				} else {
					if (ca.selected_last_time) {
						ca.count++;
						System.out
								.println("thsi color area has been selected for "
										+ ca.count + " consecutive times");
					}
					ca.setBorder(BorderFactory.createLineBorder(Color.blue, 4));
					ca.selected = true;
				}
			}

		}

	}

	class evolveActionListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {

			if (evolve_version == single_father) {
				ColorArea father = null;
				for (ColorArea ca : ColorAreas) {
					if (ca.selected) {
						father = ca;
						break;
					}
				}
				if (father == null) {
					JOptionPane.showMessageDialog(null,
							"Sorry, you need to choose a father.", "update",
							JOptionPane.WARNING_MESSAGE);
				} else {
					for (ColorArea ca : ColorAreas) {
						if (ca.equals(father)) {
							continue;
						} else {// each one mate with the chosen father

							int method = default_crossover_method;
							switch (method) {
							case GUI.UNIFORM:
								GeneticAlgorithm.cross_over_uniform(father, ca);
								break;
							case GUI.SingleFixed:
								GeneticAlgorithm.cross_over_subarea_level(
										father, ca);
								break;
							case GUI.SingleRandomC:
								GeneticAlgorithm
										.cross_over_random_single_point_random_color_level(
												father, ca);
								break;
							case GUI.SingleRandomCA:
								GeneticAlgorithm
										.cross_over_random_singlePoint_color_area_level(
												father, ca);
								break;
							case GUI.SingleRandomSA:
								GeneticAlgorithm
										.cross_over_random_single_point_subarea_level(
												father, ca);
								break;

							default:
								break;
							}
							GeneticAlgorithm.mutate(mutationRate, ca);
							// ca.gene.show_gene();

							ca.update_color();
							// ca_chosed = null;
							// father.setBorder(BorderFactory.createLineBorder(
							// Color.white, 4));
							// father.selected = false;
						}
					}
				}
			}

			if (evolve_version == multi_parents) {
				fathers.clear();
				for (ColorArea ca : ColorAreas) {
					if (ca.selected) {
						fathers.add(ca);
					}
				}
				System.out.println("fathers size " + fathers.size());
				if (fathers.size() == 0) {
					JOptionPane.showMessageDialog(null,
							"You need to selected at least one parent!",
							"Warning", JOptionPane.WARNING_MESSAGE);
				} else {
					// JOptionPane.showMessageDialog(null,
					// "multi not implemented yet", "sorry",
					// JOptionPane.WARNING_MESSAGE);
					for (ColorArea ca : ColorAreas) {
						if (!ca.selected) {
							ColorArea father = choose_fathers(fathers);
							int method = default_crossover_method;
							switch (method) {
							case GUI.UNIFORM:
								GeneticAlgorithm.cross_over_uniform(father, ca);
								break;
							case GUI.SingleFixed:
								GeneticAlgorithm.cross_over_subarea_level(
										father, ca);
								break;
							case GUI.SingleRandomC:
								GeneticAlgorithm
										.cross_over_random_single_point_random_color_level(
												father, ca);
								break;
							case GUI.SingleRandomCA:
								GeneticAlgorithm
										.cross_over_random_singlePoint_color_area_level(
												father, ca);
								break;
							case GUI.SingleRandomSA:
								GeneticAlgorithm
										.cross_over_random_single_point_subarea_level(
												father, ca);
								break;

							default:
								break;
							}
							GeneticAlgorithm.mutate(mutationRate, ca);
							// ca.gene.show_gene();
							ca.update_color();
						}
					}

				}

				// for (ColorArea ca : fathers) {
				// ca.setBorder(BorderFactory.createLineBorder(Color.white, 4));
				// ca.selected = false;
				// ca.selected_last_time = true;
				// }
			}

		}
	}

	private ColorArea choose_fathers(LinkedList<ColorArea> fathers) {
		if (fathers.size() == 0) {
			System.out
					.println("choose father function, need at least one father");
			return null;
		}
		if (fathers.size() == 1) {
			return fathers.getFirst();
		}
		double[] possibilities = new double[fathers.size()];
		double total_count = 0;
		for (ColorArea father : fathers) {
			total_count += father.count;
		}
		total_count += fathers.size();
		double sum = 0;
		for (int i = 0; i < possibilities.length; i++) {
			possibilities[i] = (double) (fathers.get(i).count + 1)
					/ (double) (total_count);
			sum += possibilities[i];
		}
		// System.out.println(sum);
		int index = 0;
		double poss = 0;
		do {
			index = rand.nextInt(fathers.size());
			poss = rand.nextDouble();
		} while (poss > possibilities[index]);
		return fathers.get(index);

	}

	class crossOverSettingActionListener implements ActionListener {
		int method;
		JLabel label;

		public crossOverSettingActionListener(int method, JLabel label) {
			this.method = method;
			this.label = label;
			// TODO Auto-generated constructor stub
		}

		public void actionPerformed(ActionEvent event) {
			// TODO Auto-generated method stub
			default_crossover_method = method;
			switch (method) {
			case GUI.UNIFORM:
				label.setText("UNIFORM");
				break;
			case GUI.SingleFixed:
				label.setText("Single Fixed");

				break;
			case GUI.SingleRandomC:
				label.setText("Single Random Color Level");

				break;
			case GUI.SingleRandomCA:
				label.setText("Single Random Color Area Level");

				break;
			case GUI.SingleRandomSA:
				label.setText("Single Random Sub Area Level");

				break;

			default:
				break;
			}

		}
	}

	class EvolveSettingActionListener implements ActionListener {
		int version;
		JLabel label;

		public EvolveSettingActionListener(int version, JLabel label) {
			// TODO Auto-generated constructor stub
			this.version = version;
			this.label = label;
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			// TODO Auto-generated method stub
			evolve_version = version;
			switch (version) {
			case single_father:
				for (ColorArea ca : ColorAreas) {
					ca.selected = false;
					ca.selected_again = false;
					ca.count = 0;
				}
				ca_chosed = null;
				for (ColorArea ca : ColorAreas) {
					ca.setBorder(BorderFactory.createLineBorder(Color.white, 4));
				}
				label.setText("Single Father");
				break;
			case multi_parents:
				for (ColorArea ca : ColorAreas) {
					ca.setBorder(BorderFactory.createLineBorder(Color.white, 4));
					// if (ca.selected) {
					// ca.selected_last_time = true;
					//
					// }
					ca.selected = false;
				}

				// for (ColorArea ca : ColorAreas) {
				// ca.selected_again = false;
				//
				// }
				label.setText("Multi Parents");
				break;

			default:
				break;
			}

		}
	}
}
